package top.codef.serializers;

import java.io.IOException;
import java.math.BigDecimal;

import javax.money.MonetaryAmount;
import javax.money.format.MonetaryAmountFormat;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import top.codef.anno.Money;
import top.codef.enums.MoneyFormatStyle;
import top.codef.properties.MercuryProperties;

public abstract class AbstractMoneySerializer<T extends MonetaryAmount> extends StdSerializer<T>
		implements ContextualSerializer {

	protected final MercuryProperties mercuryProperties;

	protected MoneyFormatStyle style = MoneyFormatStyle.DEFAULT;

	protected final MonetaryAmountFormat amountFormat;

	private static final long serialVersionUID = 5951061268723354778L;

	protected AbstractMoneySerializer(Class<? extends MonetaryAmount> t, boolean dummy,
			MercuryProperties mercuryProperties, MonetaryAmountFormat amountFormat) {
		super(t, dummy);
		this.mercuryProperties = mercuryProperties;
		this.amountFormat = amountFormat;
	}

	public AbstractMoneySerializer(Class<T> t, MercuryProperties mercuryProperties, MonetaryAmountFormat amountFormat) {
		super(t);
		this.mercuryProperties = mercuryProperties;
		this.amountFormat = amountFormat;
	}

	public AbstractMoneySerializer(JavaType type, MercuryProperties mercuryProperties,
			MonetaryAmountFormat amountFormat) {
		super(type);
		this.mercuryProperties = mercuryProperties;
		this.amountFormat = amountFormat;
	}

	public AbstractMoneySerializer(StdSerializer<? extends MonetaryAmount> src, MercuryProperties mercuryProperties,
			MonetaryAmountFormat amountFormat) {
		super(src);
		this.mercuryProperties = mercuryProperties;
		this.amountFormat = amountFormat;
	}

	public AbstractMoneySerializer(StdSerializer<? extends MonetaryAmount> src, MercuryProperties mercuryProperties,
			MoneyFormatStyle style, MonetaryAmountFormat amountFormat) {
		super(src);
		this.mercuryProperties = mercuryProperties;
		this.style = style;
		this.amountFormat = amountFormat;
	}

	protected abstract AbstractMoneySerializer<?> withStyle(MoneyFormatStyle moneyFormatStyle);

	@Override
	public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
			throws JsonMappingException {
		AbstractMoneySerializer<?> ser = this;
		if (property != null) {
			Money money = property.getAnnotation(Money.class);
			MoneyFormatStyle overrideStyle = MoneyStyles.out(money);
			if (money != null && overrideStyle != style) {
				ser = ser.withStyle(overrideStyle);
			}
		}
		return ser;
	}

	@Override
	public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException {
		if (value == null)
			gen.writeNull();
		else {
			switch (style) {
			case DEFAULT:
				gen.writeString(amountFormat.format(value));
				break;
			case NUM_ONLY:
				gen.writeString(value.getNumber().toString());
				break;
			case OBJECT:
				gen.writeStartObject(value);
				gen.writeStringField("amount", new BigDecimal(value.getNumber().toString()).toString());
				gen.writeStringField("unit", value.getCurrency().getCurrencyCode());
				gen.writeEndObject();
				break;
			}
		}
	}
}
